package com.mtw.bbs.gateway.filter;


import com.alibaba.fastjson.JSON;
import com.mtw.bbs.common.core.enums.ResultCode;
import com.mtw.bbs.common.core.security.UserDetails;
import com.mtw.bbs.common.core.vo.Result;
import com.mtw.bbs.gateway.feign.AuthClient;
import com.mtw.bbs.common.core.constant.AuthConstant;
import com.mtw.bbs.gateway.config.AuthorizeUrlsConfig;
import com.mtw.bbs.gateway.util.ResponseUtils;
import com.nimbusds.jose.JWSObject;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.context.annotation.Lazy;
import org.springframework.core.Ordered;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CompletableFuture;

/**
 * 安全拦截全局过滤器
 */
@Slf4j
@Component
public class SecurityGlobalFilter implements GlobalFilter, Ordered {

    @Autowired
    private AuthorizeUrlsConfig authorizeUrlsConfig;

    @Lazy
    @Autowired
    @Qualifier("com.mtw.bbs.gateway.feign.AuthClient")
    private  AuthClient authClient;



    @SneakyThrows
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {

        ServerHttpRequest request = exchange.getRequest();
        ServerHttpResponse response = exchange.getResponse();
        // 获取请求路径
        String path = exchange.getRequest().getURI().getPath();
        // 获取JWT
        String token = request.getHeaders().getFirst(AuthConstant.AUTHORIZATION_KEY);
        final String ftoken = token;


        // 白名单,放行
        if (authorizeUrlsConfig.isPermit(path)) {
            String userInfo = null;
            // 如果请求携带token，校验token合法性
            if (StringUtils.isNotBlank(token)){
                CompletableFuture<Result<UserDetails>> future = CompletableFuture.supplyAsync(() -> {
                    HashMap<String, Object> param = new HashMap<>(1);
                    param.put("token",ftoken);
                    return  authClient.validate(param);
                });
                Result<UserDetails> userResult = future.get();
                if (userResult.isSuccess()){
                    userInfo = JSON.toJSONString(userResult.getData());
                }
            }
            //移除请求头，如果不.build()则是在原request上移除
            exchange = exchange.mutate().request(removeHeader(exchange,userInfo)).build();
            // 放行
            return chain.filter(exchange);
        }


        // token相关
        if (authorizeUrlsConfig.isTokenUrl(path)){
            exchange = exchange.mutate().request(removeHeader(exchange,"")).build();
            return chain.filter(exchange);
        }

        // 请求没有JWT
        if (StringUtils.isBlank(token)) {
            return ResponseUtils.writeErrorInfo(response, HttpStatus.UNAUTHORIZED, Result.fail(ResultCode.NO_AUTH));
        }
        // token是否过期
        try {
            JWSObject jwsObject = JWSObject.parse(token);
            Map<String, Object> payload = JSON.parseObject(jwsObject.getPayload().toString(), HashMap.class);
            if (payload == null || payload.get("exp") == null || (Long) payload.get("exp") <= System.currentTimeMillis()) {
                return ResponseUtils.writeErrorInfo(response, HttpStatus.UNAUTHORIZED, Result.fail(ResultCode.AUTH_EXP_ERROR));
            }
        } catch (Exception e) {
            return ResponseUtils.writeErrorInfo(response, HttpStatus.INTERNAL_SERVER_ERROR, Result.fail(ResultCode.SYSTEM_ERROR));
        }


        // 需要登录
        if (authorizeUrlsConfig.isAuthenticationUrl(path)) {
            String userInfo = null;
            CompletableFuture<Result<UserDetails>> future = CompletableFuture.supplyAsync(() -> {
                HashMap<String, Object> param = new HashMap<>(1);
                param.put("token",ftoken);
                return  authClient.validate(param);
            });
            Result<UserDetails> userResult = future.get();

            if (userResult.isFail()){
                return ResponseUtils.writeErrorInfo(response, HttpStatus.UNAUTHORIZED, Result.fail(ResultCode.NO_AUTH_RESOURCE.getCode(),userResult.getMesg()));
            }
            userInfo = JSON.toJSONString(userResult.getData());
            //移除请求头，如果不.build()则是在原request上移除
            exchange = exchange.mutate().request(removeHeader(exchange,userInfo)).build();
            // 放行
            return chain.filter(exchange);
        }

        // 鉴权
        if (authorizeUrlsConfig.isResourceUrl(path)) {
            CompletableFuture<Result<UserDetails>> future = CompletableFuture.supplyAsync(() -> {
                HashMap<String, Object> param = new HashMap<>(3);
                // 请求路径
                param.put("url",request.getURI().getPath());
                // 请求方式
                param.put("method",request.getMethod().name());
                // 请求携带的token
                param.put("token",ftoken);
                return authClient.permission(param);
            });
            Result<UserDetails> result = future.get();
            if (result.isFail()){
                return ResponseUtils.writeErrorInfo(response, HttpStatus.UNAUTHORIZED, result);
            }
            // 将用户信息放入请求中
            String userInfo = JSON.toJSONString(result.getData());
            ServerHttpRequest httpRequest = removeHeader(exchange,userInfo);
            exchange = exchange.mutate().request(httpRequest).build();
            return chain.filter(exchange);
        }

        return ResponseUtils.writeErrorInfo(response, HttpStatus.UNAUTHORIZED, Result.fail(ResultCode.NO_AUTH_RESOURCE));
    }


    /**
     * 移除请求头
     *
     * @param exchange
     * @return
     */
    private ServerHttpRequest removeHeader(ServerWebExchange exchange,String userInfo) {
        return exchange.getRequest().mutate().headers(httpHeaders -> {
                    String jwt = httpHeaders.getFirst(AuthConstant.AUTHORIZATION_KEY);
                    jwt = StringUtils.isBlank(jwt) || StringUtils.isEmpty(userInfo) ? "ignore" : jwt;
                    httpHeaders.remove(AuthConstant.AUTHORIZATION_KEY);
                    // 服务间的token
                    httpHeaders.add(AuthConstant.SERVICE_AUTH_KEY, jwt);
                    // 当前用户信息
                    if (StringUtils.isNotBlank(userInfo)){
                        httpHeaders.add(AuthConstant.AUTH_USER_KEY, userInfo);
                    }
                }
        ).build();
    }


    @Override
    public int getOrder() {
        return -1;
    }
}
